/*
* This is eMonocot, a global online biodiversity information resource.
*
* Copyright © 2011–2015 The Board of Trustees of the Royal Botanic Gardens, Kew and The University of Oxford
*
* eMonocot is free software: you can redistribute it and/or modify it under the terms of the
* GNU Affero General Public License as published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.
*
* eMonocot is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* The complete text of the GNU Affero General Public License is in the source repository as the file
* ‘COPYING’. It is also available from <http://www.gnu.org/licenses/>.
*/
package org.emonocot.persistence.dao.jdbc;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
import org.emonocot.persistence.dao.JobInstanceDao;
import org.springframework.batch.core.JobInstance;
import org.springframework.batch.core.JobParameter;
import org.springframework.batch.core.JobParameter.ParameterType;
import org.springframework.batch.core.JobParameters;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.stereotype.Repository;
/**
*
* @author ben
*
*/
@Repository
public class JobInstanceDaoImpl extends JdbcDaoSupport implements
JobInstanceDao {
/**
*
* @param dataSource
* Set the data source
*/
@Autowired
public void setDatasource(DataSource dataSource) {
super.setDataSource(dataSource);
}
/**
*
* @param identifier
* the identifier of the job
* @return a job execution
*/
public final JobInstance load(final Long identifier) {
JobParameters jobParameters = getJobParameters(identifier);
RowMapper<JobInstance> rowMapper = new JobInstanceRowMapper(jobParameters);
JobInstance jobInstance = getJdbcTemplate()
.queryForObject(
"SELECT JOB_INSTANCE_ID, JOB_NAME, VERSION from BATCH_JOB_INSTANCE where JOB_INSTANCE_ID = ?",
rowMapper, identifier);
return jobInstance;
}
@Override
public List<JobInstance> list(Integer page, Integer size) {
RowMapper<JobInstance> rowMapper = new JobInstanceRowMapper();
if (size == null && page == null) {
return getJdbcTemplate().query("SELECT JOB_INSTANCE_ID, JOB_NAME, VERSION from BATCH_JOB_INSTANCE", rowMapper);
} else if (page == null) {
return getJdbcTemplate().query("SELECT JOB_INSTANCE_ID, JOB_NAME, VERSION from BATCH_JOB_INSTANCE LIMIT ?", rowMapper,size);
} else {
return getJdbcTemplate().query("SELECT JOB_INSTANCE_ID, JOB_NAME, VERSION from BATCH_JOB_INSTANCE LIMIT ? OFFSET ?", rowMapper,size, page * size);
}
}
/**
*
* @param id
* The id to delete
*/
public final void delete(final Long id) {
getJdbcTemplate().update(
"DELETE from BATCH_JOB_PARAMS where JOB_INSTANCE_ID = ?", id);
getJdbcTemplate().update(
"DELETE from BATCH_JOB_INSTANCE where JOB_INSTANCE_ID = ?", id);
}
/**
*
* @param jobInstance
* The jobExecution to save
*/
public final void save(final JobInstance jobInstance) {
String jobKey = createJobKey(jobInstance.getJobParameters());
getJdbcTemplate().update(
"INSERT into BATCH_JOB_INSTANCE(JOB_INSTANCE_ID, JOB_NAME, VERSION, JOB_KEY)"
+ " values (?, ?, ?, ?)", jobInstance.getId(),
jobInstance.getJobName(), jobInstance.getVersion(), jobKey);
for (String key : jobInstance.getJobParameters().getParameters()
.keySet()) {
JobParameter jobParameter = jobInstance.getJobParameters()
.getParameters().get(key);
insertParameter(jobInstance.getId(), jobParameter.getType(), key,
jobParameter.getValue());
}
}
/**
*
* @param jobParameters Set the job parameters
* @return the generated job key
*/
private String createJobKey(final JobParameters jobParameters) {
Map<String, JobParameter> props = jobParameters.getParameters();
StringBuffer stringBuffer = new StringBuffer();
List<String> keys = new ArrayList<String>(props.keySet());
Collections.sort(keys);
for (String key : keys) {
stringBuffer.append(key + "=" + props.get(key).toString()
+ ";");
}
MessageDigest digest;
try {
digest = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException(
"MD5 algorithm not available. Fatal (should be in the JDK).");
}
try {
byte[] bytes = digest.digest(stringBuffer.toString()
.getBytes("UTF-8"));
return String.format("%032x", new BigInteger(1, bytes));
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException(
"UTF-8 encoding not available. Fatal (should be in the JDK).");
}
}
/**
* Convenience method that inserts an individual records into the
* JobParameters table.
* @param jobId Set the job id
* @param type Set the parameter type
* @param key Set the parameter name
* @param value Set the parameter value
*/
private void insertParameter(final Long jobId, final ParameterType type,
final String key, final Object value) {
Object[] args = new Object[0];
int[] argTypes = new int[] {Types.BIGINT, Types.VARCHAR,
Types.VARCHAR, Types.VARCHAR, Types.TIMESTAMP, Types.BIGINT,
Types.DOUBLE };
if (type == ParameterType.STRING) {
args = new Object[] {jobId, key, type, value, new Timestamp(0L),
0L, 0D };
} else if (type == ParameterType.LONG) {
args = new Object[] {jobId, key, type, "", new Timestamp(0L),
value, new Double(0) };
} else if (type == ParameterType.DOUBLE) {
args = new Object[] {jobId, key, type, "", new Timestamp(0L), 0L,
value };
} else if (type == ParameterType.DATE) {
args = new Object[] {jobId, key, type, "", value, 0L, 0D };
}
getJdbcTemplate().update("INSERT into BATCH_JOB_PARAMS(JOB_INSTANCE_ID, KEY_NAME, TYPE_CD, "
+ "STRING_VAL, DATE_VAL, LONG_VAL, DOUBLE_VAL) values (?, ?, ?, ?, ?, ?, ?)", args, argTypes);
}
/**
*
* @author ben
*
*/
public class JobInstanceRowMapper implements RowMapper<JobInstance> {
/**
*
*/
private JobParameters jobParameters = new JobParameters();
/**
*
* @param newJobParameters Set the job parameters
*/
public JobInstanceRowMapper(final JobParameters newJobParameters) {
this.jobParameters = newJobParameters;
}
public JobInstanceRowMapper() {
}
/**
* @param resultSet
* Set the result set
* @param rowNumber
* Set the row number
* @throws SQLException
* if there is a problem
* @return a job execution instance
*/
public final JobInstance mapRow(final ResultSet resultSet,
final int rowNumber) throws SQLException {
JobInstance jobInstance = new JobInstance(resultSet.getBigDecimal(
"JOB_INSTANCE_ID").longValue(), jobParameters,
resultSet.getString("JOB_NAME"));
BigDecimal version = resultSet.getBigDecimal("VERSION");
if (version != null) {
jobInstance.setVersion(version.intValue());
}
return jobInstance;
}
}
/**
* @param instanceId Set the Job instance id
* @return the job parameters for that job instance
*/
private JobParameters getJobParameters(final Long instanceId) {
final Map<String, JobParameter> map
= new HashMap<String, JobParameter>();
RowCallbackHandler rowCallbackHandler = new RowCallbackHandler() {
public void processRow(final ResultSet rs) throws SQLException {
ParameterType type = ParameterType.valueOf(rs.getString(3));
JobParameter value = null;
if (type == ParameterType.STRING) {
value = new JobParameter(rs.getString(4));
} else if (type == ParameterType.LONG) {
value = new JobParameter(rs.getLong(6));
} else if (type == ParameterType.DOUBLE) {
value = new JobParameter(rs.getDouble(7));
} else if (type == ParameterType.DATE) {
value = new JobParameter(rs.getTimestamp(5));
}
// No need to assert that value is not null because it's an enum
map.put(rs.getString(2), value);
}
};
getJdbcTemplate().query("SELECT JOB_INSTANCE_ID, KEY_NAME, TYPE_CD, "
+ "STRING_VAL, DATE_VAL, LONG_VAL, DOUBLE_VAL from BATCH_JOB_PARAMS where JOB_INSTANCE_ID = ?", rowCallbackHandler, instanceId);
return new JobParameters(map);
}
}